package com.orion.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * redis工具类（redis版本3.0或以上）
 * @author li.lc
 * @date  2019-06-06
 */
@Slf4j
@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     *维护一个本类的静态变量
      */
    private static RedisUtil redisUtil;

    @PostConstruct
    public void init() {
        redisUtil = this;
        redisUtil.redisTemplate = this.redisTemplate;
    }

    public static RedisTemplate getRedisTemplate(){
        return redisUtil.redisTemplate;
    }

    //****************************************************字符串****************************************************
    /**
     * 将字符串值 value 关联到 key 。
     * 如果 key 已经持有其他值， SET 就覆写旧值， 无视类型。
     * 当 SET 命令对一个带有生存时间（TTL）的键进行设置之后， 该键原有的 TTL 将被清除。
     * @param key 键
     * @param value 值
     */
    public static void set(String key,Object value){
        redisUtil.redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 只在键 key 不存在的情况下， 将键 key 的值设置为 value 。
     * 若键 key 已经存在， 则 SETNX 命令不做任何动作。
     * @param key 键
     * @param value 值
     * @return 是否设置成功
     */
    public static Boolean setNx(String key,Object value){
        return redisUtil.redisTemplate.opsForValue().setIfAbsent(key,value);
    }

    /**
     * 将键 key 的值设置为 value ， 并将键 key 的生存时间设置为 seconds 秒钟。
     * @param key 键
     * @param value 值
     * @param expireSeconds 过期秒数
     */
    public static void setEx(String key,Object value,long expireSeconds){
        if (expireSeconds>0) {
            redisUtil.redisTemplate.opsForValue().set(key, value, expireSeconds, TimeUnit.SECONDS);
        }else {
            redisUtil.redisTemplate.opsForValue().set(key,value);
        }
    }

    /**
     * 将键 key 的值设置为 value ， 并将键 key 的生存时间设置为 milliseconds  秒钟。
     * @param key 键
     * @param value 值
     * @param expireMilliseconds 过期毫秒数
     */
    public static void pSetEx(String key,Object value,long expireMilliseconds){
        if (expireMilliseconds>0) {
            redisUtil.redisTemplate.opsForValue().set(key, value, expireMilliseconds, TimeUnit.MILLISECONDS);
        }else {
            redisUtil.redisTemplate.opsForValue().set(key,value);
        }
    }

    /**
     * 获取与指定键相关的值
     * @param key 键
     * @param clazz 值的class类型
     * @return clazz类型结果
     */
    public static <T> T get(String key,Class<T> clazz) {
        Object result = redisUtil.redisTemplate.opsForValue().get(key);
        return cast(result,clazz);
    }

    /**
     * 将键 key 的值设为 value ， 并返回键 key 在被设置之前的旧值
     * @param key 键
     * @param value 值
     * @param clazz 旧值的class类型
     * @return clazz类型旧值结果
     */
    public static <T> T getSet(String key,Object value ,Class<T> clazz){
        Object result = redisUtil.redisTemplate.opsForValue().getAndSet(key,value);
        return cast(result,clazz);
    }

    /**
     * 返回键 key 储存的字符串值的长度
     * @param key 键
     * @return 追加 value 之后， 键 key 的值的长度。
     */
    public static Long strLen(String key){
        return redisUtil.redisTemplate.opsForValue().size(key);
    }

    /**
     * 如果键 key 已经存在并且它的值是一个字符串， APPEND 命令将把 value 追加到键 key 现有值的末尾。
     * 如果 key 不存在， APPEND 就简单地将键 key 的值设为 value ， 就像执行 SET key value 一样。
     * @param key 键
     * @param value 值
     * @return 追加 value 之后， 键 key 的值的长度
     */
    public static Integer append(String key ,String value){
        return redisUtil.redisTemplate.opsForValue().append(key, value);
    }

    /**
     * 返回键 key 储存的字符串值的指定部分， 字符串的截取范围由 start 和 end 两个偏移量决定 (包括 start 和 end 在内)。
     * 负数偏移量表示从字符串的末尾开始计数， -1 表示最后一个字符， -2 表示倒数第二个字符， 以此类推。
     * 不超过实际字符串的值域来处理超出范围的值域请求
     * @param key 键
     * @param start 开始索引
     * @param end 结束索引
     * @return 返回字符串值的指定部分
     */
    public static String getRange(String key,Integer start,Integer end){
        return redisUtil.redisTemplate.opsForValue().get(key,start,end);
    }

    /**
     * 递增key的值
     * @param key 键
     * @return 递增值
     */
    public static Long incr(String key){
        return redisUtil.redisTemplate.opsForValue().increment(key,1);
    }

    /**
     * 按照步长递增key的值
     * @param key 键
     * @param delta 步长
     * @return 递增值
     */
    public static Long incrBy(String key,long delta){
        if (delta < 0){
            throw new RuntimeException("步长必须大于0");
        }
        return redisUtil.redisTemplate.opsForValue().increment(key,delta);
    }

    /**
     * 递减key的值
     * @param key 键
     * @return 递增值
     */
    public static Long decr(String key){
        return redisUtil.redisTemplate.opsForValue().increment(key,-1);
    }

    /**
     * 按照步长递减key的值
     * @param key 键
     * @param delta 步长
     * @return 递增值
     */
    public static Long decrBy(String key ,long delta){
        if (delta < 0){
            throw new RuntimeException("步长必须大于0");
        }
        return redisUtil.redisTemplate.opsForValue().increment(key,-delta);
    }

    /**
     * 同时为多个键设置值。如果某个给定键已经存在， 那么 MSET 将使用新值去覆盖旧值，
     * @param map 键值对map
     */
    public static void mSet(Map<String,Object> map){
        redisUtil.redisTemplate.opsForValue().multiSet(map);
    }

    /**
     * 当且仅当所有给定键都不存在时， 为所有给定键设置值。
     * 即使只有一个给定键已经存在， MSETNX 命令也会拒绝执行对所有键的设置操作。
     * MSETNX 是一个原子性(atomic)操作， 所有给定键要么就全部都被设置， 要么就全部都不设置， 不可能出现第三种状态。
     * @param map 键值对map
     * @return 当所有给定键都设置成功时，命令返回true ；
     *          如果因为某个给定键已经存在而导致设置未能成功执行，那么命令返回 false
     */
    public static Boolean mSetNx(Map<String,Object> map){
        return redisUtil.redisTemplate.opsForValue().multiSetIfAbsent(map);
    }

    /**
     * 返回给定的一个或多个字符串键的值
     * @param keys 键集合
     * @param clazz 值的class类型
     * @return 所有给定键class类型的值
     */
    public static <T> List<T> mGet(List<String> keys,Class<T> clazz){
        List<Object> objectList = redisUtil.redisTemplate.opsForValue().multiGet(keys);
        return listFilterNullVal(objectList,clazz);
    }

    /**
     * 返回给定的一个或多个字符串键的值，类型需要自行转换
     * @param keys 键集合
     * @return 所有给定键的值
     */
    public static List<Object> mGet(List<String> keys){
        return redisUtil.redisTemplate.opsForValue().multiGet(keys);
    }

    //****************************************************哈希表****************************************************
    /**
     * 如果给定的哈希表并不存在， 那么一个新的哈希表将被创建并执行 HSET 操作
     * 如果域 field 已经存在于哈希表中， 那么它的旧值将被新值 value 覆盖
     * @param key 键
     * @param field 域
     * @param value 值
     */
    public static void hSet(String key,String field,Object value){
        redisUtil.redisTemplate.opsForHash().put(key,field,value);
    }

    /**
     * 当且仅当域 field 尚未存在于哈希表的情况下， 将它的值设置为 value 。
     * 如果给定域已经存在于哈希表当中， 那么命令将放弃执行设置操作。
     * 如果哈希表 hash 不存在， 那么一个新的哈希表将被创建并执行 HSETNX 命令。
     * @param key 键
     * @param field 域
     * @param value 值
     * @return
     */
    public static Boolean hSetNx(String key,String field,String value){
        return redisUtil.redisTemplate.opsForHash().putIfAbsent(key,field,value);
    }

    /**
     * 返回哈希表中给定域的值
     * @param key 键
     * @param field 域
     * @param clazz 值的class类型
     * @return 返回给定域指定class类型的值
     */
    public static <T> T hGet(String key,String field,Class<T> clazz){
        Object result = redisUtil.redisTemplate.opsForHash().get(key,field);
        return cast(result,clazz);
    }

    /**
     * 检查给定域 field 是否存在于哈希表 hash 当中
     * @param key 键
     * @param field 域
     * @return field是否存在
     */
    public static Boolean hExist(String key,String field){
        return redisUtil.redisTemplate.opsForHash().hasKey(key,field);
    }

    /**
     * 删除哈希表 key 中的一个或多个指定域，不存在的域将被忽略
     * @param key 键
     * @param fields 域集合
     * @return 被成功移除的域的数量，不包括被忽略的域
     */
    public static Long hDel(String key,String... fields){
        return redisUtil.redisTemplate.opsForHash().delete(key,fields);
    }

    /**
     * 返回哈希表 key 中域的数量
     * @param key 键
     * @return 返回哈希表 key 中域的数量
     */
    public static Long hLen(String key){
        return redisUtil.redisTemplate.opsForHash().size(key);
    }

    /**
     * 为哈希表 key 中的域 field 的值加上增量
     * @param key 键
     * @param field 域
     * @param delta 步长，可以为负数，即为减法
     * @return 返回哈希表 key 中域的数量
     */
    public static Long hIncrBy(String key,String field,Integer delta){
        return redisUtil.redisTemplate.opsForHash().increment(key,field,delta);
    }

    /**
     * 同时将多个 field-value (域-值)对设置到哈希表 key 中，此命令会覆盖哈希表中已存在的域
     * 如果 key 不存在，一个空哈希表被创建并执行 HMSET 操作
     * @param key 键
     * @param map 域值集合
     */
    public static void hMSet(String key, Map<String,Object> map){
        redisUtil.redisTemplate.opsForHash().putAll(key,map);
    }

    /**
     * 返回哈希表 key 中，一个或多个给定域的值,只能value是同种类型的key值
     * @param key 键
     * @param fields 域集合
     * @return 返回指定域class类型的值
     */
    public static <T> List<T> hMGet(String key, List<String> fields,Class<T> clazz){
        HashOperations<String,String,Object> hashOperations = redisUtil.redisTemplate.opsForHash();
        List<Object> objectList = hashOperations.multiGet(key,fields);
        return listFilterNullVal(objectList,clazz);
    }

    /**
     * 返回哈希表 key 中，一个或多个给定域的值,value可以是不同类型的key值，需要进行手动类型转换
     * @param key 键
     * @param fields 域集合
     * @return 返回指定域的值
     */
    public static List<Object> hMGet(String key, List<Object> fields){
       return redisUtil.redisTemplate.opsForHash().multiGet(key,fields);
    }

    /**
     * 返回哈希表 key 中，所有的域和值，在尺寸不可预知/尺寸过大的Hash中避免使用
     * @param key 键
     * @return 所有的域和值
     */
    public static Map<Object,Object> hGetAll(String key){
        return redisUtil.redisTemplate.opsForHash().entries(key);
    }

    //*****************************************************列表*****************************************************

    /**
     * 将一个或多个值 value 从左到右的顺序插入依次插入到表头
     * @param key 键
     * @param values 一个或多个值
     * @return 列表长度
     */
    public static Long lPush(String key,Object... values){
        return redisUtil.redisTemplate.opsForList().leftPushAll(key,values);
    }

    /**
     * 将值 value 从左到右的顺序插入到列表 key 的表头，当且仅当 key 存在并且是一个列表，当 key 不存在时，什么也不做
     * @param key 键
     * @param value 值
     * @return 列表长度
     */
    public static Long lPushHx(String key,Object value){
        return redisUtil.redisTemplate.opsForList().leftPushIfPresent(key,value);
    }

    /**
     * 将一个或多个值 value 从右到左的顺序插入依次插入到表头
     * @param key 键
     * @param values 一个或多个值
     * @return 列表长度
     */
    public static Long rPush(String key,Object... values){
        return redisUtil.redisTemplate.opsForList().rightPushAll(key, values);
    }

    /**
     * 将值 value 从右到左的顺序插入到列表 key 的表头，当且仅当 key 存在并且是一个列表，当 key 不存在时，什么也不做
     * @param key 键
     * @param value 值
     * @return 列表长度
     */
    public static Long rPushHx(String key,Object value){
        return redisUtil.redisTemplate.opsForList().rightPushIfPresent(key,value);
    }

    /**
     * 移除并返回列表 key 的头元素
     * @param key 键
     * @param clazz 值的class类型
     * @return 列表 key 的头元素
     */
    public static <T> T lPop(String key,Class<T> clazz){
        Object result = redisUtil.redisTemplate.opsForList().leftPop(key);
        return cast(result,clazz);
    }

    /**
     * 移除并返回列表 key 的尾元素
     * @param key 键
     * @param clazz 值的class类型
     * @return 列表 key 的尾元素
     */
    public static <T> T rPop(String key,Class<T> clazz){
        Object result = redisUtil.redisTemplate.opsForList().rightPop(key);
        return cast(result,clazz);
    }

    /**
     * 列表 source 中的最后一个元素(尾元素)弹出，并返回给客户端
     * 将 source 弹出的元素插入到列表 destination ，作为 destination 列表的的头元素
     * @param source 源列表
     * @param destination 目标列表
     * @param clazz 值的class类型
     * @return 列表source 的尾元素
     */
    public static <T> T rPoplPush (String source,String destination,Class<T> clazz){
        Object result = redisUtil.redisTemplate.opsForList().rightPopAndLeftPush(source,destination);
        return cast(result,clazz);
    }

    /**
     * 根据参数 count 的值，移除列表中与参数 value 相等的元素
     * count > 0 : 从表头开始向表尾搜索，移除与 value 相等的元素，数量为 count
     * count < 0 : 从表尾开始向表头搜索，移除与 value 相等的元素，数量为 count 的绝对值
     * count = 0 : 移除表中所有与 value 相等的值
     * @param key 键
     * @param count 数量
     * @param value 值
     * @return 被移除元素的数量
     */
    public static Long lRem(String key , Integer count ,Object value){
        return redisUtil.redisTemplate.opsForList().remove(key,count, value);
    }

    /**
     * 返回列表 key 的长度
     * @param key 键
     * @return 列表长度
     */
    public static Long Llen(String key){
        return redisUtil.redisTemplate.opsForList().size(key);
    }

    /**
     * 返回列表 key 中，下标为 index 的元素
     * @param key 键
     * @param index 下标
     * @param clazz 元素类型
     * @return 列表中下标为 index 的class类型的元素
     */
    public static <T> T lIndex(String key , Long index ,Class<T> clazz){
        Object result = redisUtil.redisTemplate.opsForList().index(key,index);
        return cast(result,clazz);
    }

    /**
     * 将值 value 插入到列表 key 当中，位于值 pivot 之前
     * @param key 键
     * @param pivot 参考值
     * @param value 插入值
     * @return 列表的长度
     */
    public static Long lLeftInsert(String key ,Object pivot,Object value){
        return redisUtil.redisTemplate.opsForList().leftPush(key, pivot, value);
    }

    /**
     * 将值 value 插入到列表 key 当中，位于值 pivot 之后
     * @param key 键
     * @param pivot 参考值
     * @param value 插入值
     * @return 列表的长度
     */
    public static Long lRightInsert(String key ,Object pivot,Object value){
        return redisUtil.redisTemplate.opsForList().rightPush(key, pivot, value);
    }

    /**
     * 将列表 key 下标为 index 的元素的值设置为 value
     * @param key 键
     * @param index 下标
     * @param value 插入值
     */
    public static void lSet(String key ,Long index,Object value){
        redisUtil.redisTemplate.opsForList().set(key,index,value);
    }

    /**
     * 返回列表 key 中指定区间内的元素，区间以偏移量 start 和 stop 指定，需要进行手动类型转换
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 返回列表指定区间内的元素
     */
    public static List<Object> lRange(String key,Long start,Long end){
        return redisUtil.redisTemplate.opsForList().range(key,start,end);
    }

    /**
     * 返回列表 key 中指定区间内的相同类型的元素,不同类型将忽略，区间以偏移量 start 和 stop 指定
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 返回列表指定区间内的元素
     */
    public static <T> List<T> lRange(String key,Long start,Long end,Class<T> clazz){
        List<Object> objectList = redisUtil.redisTemplate.opsForList().range(key,start,end);
        return listFilterNullVal(objectList,clazz);
    }

    /**
     * 让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     */
    public static void lTrim(String key,Long start,Long end){
        redisUtil.redisTemplate.opsForList().trim(key,start,end);
    }

    //*****************************************************集合****************************************************
    /**
     * 将一个或多个 member 元素加入到集合 key 当中，已经存在于集合的 member 元素将被忽略
     * @param key 键
     * @param members 成员集合
     * @return 被添加到集合中的新元素的数量
     */
    public static Long sAdd(String key, Object... members){
        return redisUtil.redisTemplate.opsForSet().add(key, members);
    }

    /**
     * 判断 member 元素是否集合 key 的成员
     * @param key 键
     * @param member 成员
     * @return 是否其中成员
     */
    public static Boolean sIsMember(String key,Object member){
        return redisUtil.redisTemplate.opsForSet().isMember(key,member);
    }

    /**
     * 移除集合中的指定 key 的一个随机元素，移除后会返回移除的元素
     * @param key 键
     * @return 被移除的元素
     */
    public static Object sPop (String key){
        return redisUtil.redisTemplate.opsForSet().pop(key);
    }

    /**
     * 移除集合中的指定 key 的count个随机元素，移除后会返回移除的元素
     * @param key 键
     * @param count 数量
     * @return 被移除的元素
     */
    public static List<Object> sPop (String key,Long count){
        return redisUtil.redisTemplate.opsForSet().pop(key,count);
    }

    /**
     * 返回集合中的一个随机元素，但不会移除,区别sPop
     * @param key 键值
     * @return 随机元素
     */
    public static Object sRandMember (String key){
        return redisUtil.redisTemplate.opsForSet().randomMember(key);
    }

    /**
     * 返回集合中的count个随机元素，但不会移除,区别sPop
     * @param key 键
     * @param count 数量
     * @return 随机元素
     */
    public static List<Object> sRandMember (String key,Long count){
         return redisUtil.redisTemplate.opsForSet().randomMembers(key,count);
    }

    /**
     * 移除集合 key 中的一个或多个 member 元素，不存在的 member 元素会被忽略
     * @param key 键
     * @param members 要移除的成员集合
     * @return 被成功移除的元素的数量，不包括被忽略的元素
     */
    public static Long sRem(String key,Object... members){
        return redisUtil.redisTemplate.opsForSet().remove(key,members);
    }

    /**
     * 将 member 元素从 source 集合移动到 destination 集合,source不存在或不包含member，则不进行任何操作
     * @param source 源集合
     * @param destination 目标集合
     * @param member 被移动的成员
     * @return 是否移动成功
     */
    public static Boolean sMove(String source ,String destination,Object member){
        return redisUtil.redisTemplate.opsForSet().move(source,member,destination);
    }

    /**
     * 返回集合 key 的基数(集合中元素的数量)
     * @param key 键
     * @return 集合基数
     */
    public static Long sCard(String key){
        return redisUtil.redisTemplate.opsForSet().size(key);
    }

    /**
     * 返回集合 key 中的所有成员,需要自行转换类型
     * @param key 键
     * @return set集合
     */
    public static Set<Object> sMembers(String key){
        return redisUtil.redisTemplate.opsForSet().members(key);
    }

    /**
     * 返回集合 key 中的所有相同类型的成员，不同类型将忽略
     * @param key 键
     * @param clazz 成员类型
     * @return class类型成员的集合
     */
    public static <T> Set<T> sMembers(String key,Class<T> clazz){
        Set<Object> objectSet = redisUtil.redisTemplate.opsForSet().members(key);
        return setFilterNullVal(objectSet,clazz);
    }

    public static Set<Object> sScan(String  key ,String pattern ,Long count){
        Set<Object> resultSet = new HashSet<>();
        ScanOptions scanOptions = ScanOptions.scanOptions().match(pattern).count(count).build();
        try {
            Cursor<Object> objectCursor = redisUtil.redisTemplate.opsForSet().scan(key,scanOptions);
            while (objectCursor.hasNext()){
                resultSet.add(objectCursor.next());
            }
            objectCursor.close();
        }catch (IOException ioex){
            log.warn("sScan发生异常",ioex);
        }
        return  resultSet;
    }

    //***************************************************有序集合***************************************************
    /**
     * 将一个member 元素及其 score 值加入到有序集 key 当中
     * @param key 键
     * @param member 成员
     * @param score 分数
     * @return 是否成功增加
     */
    public static Boolean zAdd(String key,Object member ,Double score){
        return redisUtil.redisTemplate.opsForZSet().add(key,member,score);
    }

    /**
     * 将多个member 元素及其 score 值加入到有序集 key 当中
     * @param key 键
     * @param tuples {值,分数}
     * @return 被成功添加的新成员的数量，不包括那些被更新的、已经存在的成员
     */
    public static Long zAdd(String key,Set<ZSetOperations.TypedTuple<Object>> tuples){
        return redisUtil.redisTemplate.opsForZSet().add(key,tuples);
    }

    /**
     * 返回有序集 key 中，成员 member 的 score 值
     * @param key 键
     * @param member 成员
     * @return 成员分数
     */
    public static Double zScore(String key ,Object member){
        return redisUtil.redisTemplate.opsForZSet().score(key, member);
    }

    /**
     * 返回有序集 key 中，成员 member 的 score 值
     * @param key 键
     * @param member 成员
     * @return 成员新分数
     */
    public static Double zIncrBy(String key ,Object member,Double delta){
        return redisUtil.redisTemplate.opsForZSet().incrementScore(key, member,delta);
    }

    /**
     * 返回有序集 key 的基数
     * @param key 键
     * @return 集合基数
     */
    public static Long zCard(String key){
        return redisUtil.redisTemplate.opsForZSet().zCard(key);
    }

    /**
     * 返回有序集 key 中， score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量
     * @param key 键
     * @param min 下限分数
     * @param max 上限分数
     * @return score 值在 min 和 max 之间的成员的数量
     */
    public static Long zCount(String key,Double min,Double max){
        return redisUtil.redisTemplate.opsForZSet().count(key,min,max);
    }

    /**
     * 按照score升序，返回有序集 key 中，指定区间内的成员,类型需自行转化
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 区间内的成员集合
     */
    public static Set<Object> zRange(String key, Long start, Long end){
        return redisUtil.redisTemplate.opsForZSet().range(key,start,end);
    }

    /**
     * 按照score升序，返回有序集 key 中，指定区间内指定类型的成员,不符合class类型将过滤
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 区间内的class类型的成员集合
     */
    public static <T> Set<T> zRange(String key, Long start, Long end, Class<T> clazz){
        Set<Object> objectSet = redisUtil.redisTemplate.opsForZSet().range(key,start,end);
        return setFilterNullVal(objectSet,clazz);
    }

    /**
     * 按照score降序，返回有序集 key 中，指定区间内的成员,类型需自行转化
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 区间内的成员集合
     */
    public static Set<Object> zRevRange(String key, Long start, Long end){
        return redisUtil.redisTemplate.opsForZSet().range(key,start,end);
    }

    /**
     * 按照score降序，返回有序集 key 中，指定区间内指定类型的成员,不符合class类型将过滤
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 区间内的class类型的成员集合
     */
    public static <T> Set<T> zRevRange(String key, Long start, Long end, Class<T> clazz){
        Set<Object> objectSet = redisUtil.redisTemplate.opsForZSet().range(key,start,end);
        return setFilterNullVal(objectSet,clazz);
    }

    /**
     * 按照score升序，返回有序集 key 中，指定区间内的成员及其分数,类型需自行转化
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 区间内的class类型的成员及其分数集合
     */
    public static Set<ZSetOperations.TypedTuple<Object>> zRangeWithScores(String key, Long start, Long end){
        return redisUtil.redisTemplate.opsForZSet().rangeWithScores(key,start,end);
    }

    /**
     * 按照score升序，返回有序集 key 中，指定区间内指定类型的成员及其分数,成员不符合class类型将过滤
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 区间内的class类型的成员及其分数集合
     */
    public static <T> Set<ZSetOperations.TypedTuple<T>> zRangeWithScores(String key, Long start, Long end, Class<T> clazz){
        Set<ZSetOperations.TypedTuple<Object>> objectSet = redisUtil.redisTemplate.opsForZSet().rangeWithScores(key,start,end);
        Set<ZSetOperations.TypedTuple<T>> resultSet;
        if (objectSet != null && !objectSet.isEmpty()) {
            resultSet = new HashSet<>(objectSet.size());
            objectSet.forEach(item -> {
                T tempValue = cast(item.getValue(),clazz);
                if (tempValue != null) {
                    DefaultTypedTuple<T> defaultTypedTuple = new DefaultTypedTuple<>(tempValue,item.getScore());
                    resultSet.add(defaultTypedTuple);
                }
            });
        }else {
            resultSet = Collections.emptySet();
        }
        return resultSet;
    }

    /**
     * 按照score降序，返回有序集 key 中，指定区间内的成员以及分数,成员类型需自行转化
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 返回指定区间的成员及其分数
     */
    public static Set<ZSetOperations.TypedTuple<Object>> zRevRangeWithScores(String key, Long start, Long end){
        return redisUtil.redisTemplate.opsForZSet().reverseRangeWithScores(key,start,end);
    }

    /**
     * 按照score降序，返回有序集 key 中，指定区间内指定类型的成员,成员不符合class类型将过滤
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 返回指定区间的class类型的成员及其分数
     */
    public static <T> Set<ZSetOperations.TypedTuple<T>> zRevRangeWithScores(String key, Long start, Long end,Class<T> clazz){
        Set<ZSetOperations.TypedTuple<Object>> objectSet = redisUtil.redisTemplate.opsForZSet().rangeWithScores(key,start,end);
        Set<ZSetOperations.TypedTuple<T>> resultSet;
        if (objectSet != null && !objectSet.isEmpty()) {
            resultSet = new HashSet<>(objectSet.size());
            objectSet.forEach(item -> {
                T tempValue = cast(item.getValue(),clazz);
                if (tempValue != null) {
                    resultSet.add(new DefaultTypedTuple<>(tempValue,item.getScore()));
                }
            });
        }else {
            resultSet = Collections.emptySet();
        }
        return resultSet;
    }

    /**
     * 按照升序，返回有序集 key 中成员 member 的排名
     * @param key 键
     * @param member 成员
     * @return 升序成员的排名
     */
    public static Long zRank(String key ,Object member){
        return redisUtil.redisTemplate.opsForZSet().rank(key, member);
    }

    /**
     * 按照降序序，返回有序集 key 中成员 member 的排名
     * @param key 键
     * @param member 成员
     * @return 降序成员的排名
     */
    public static Long zRevRank(String key ,Object member){
        return redisUtil.redisTemplate.opsForZSet().reverseRank(key, member);
    }

    /**
     * 移除有序集 key 中的一个或多个成员，不存在的成员将被忽略
     * @param key 键
     * @param members 成员集合
     * @return 被成功移除的成员的数量，不包括被忽略的成员
     */
    public static Long zRem(String key,Object... members){
        return redisUtil.redisTemplate.opsForZSet().remove(key,members);
    }

    /**
     * 移除有序集 key 中，指定排名(rank)区间内的所有成员
     * @param key 键
     * @param start 开始下标
     * @param end 结束下标
     * @return 被移除成员的数量
     */
    public static Long zRemRangeByRank(String key ,Long start ,Long end){
        return redisUtil.redisTemplate.opsForZSet().removeRange(key,start,end);
    }

    /**
     * 移除有序集 key 中，所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员
     * @param key 键
     * @param min 最小分数
     * @param max 最大分数
     * @return 被移除成员的数量
     */
    public static Long zRemRangeByScore(String key ,Double min ,Double max){
        return redisUtil.redisTemplate.opsForZSet().removeRangeByScore(key,min,max);
    }

    public static Set<ZSetOperations.TypedTuple<Object>> zScan(String key,String pattern,Long count){
        Set<ZSetOperations.TypedTuple<Object>> resultSet = new HashSet<>();
        ScanOptions scanOptions = ScanOptions.scanOptions().match(pattern).count(count).build();
        try {
            Cursor<ZSetOperations.TypedTuple<Object>> objectCursor = redisUtil.redisTemplate.opsForZSet().scan(key,scanOptions);

            while (objectCursor.hasNext()){
                objectCursor.forEachRemaining(item -> resultSet.add(item));
                //objectCursor = redisUtil.redisTemplate.opsForZSet().
            }
            objectCursor.close();
        }catch (Exception ioex){
            log.warn("sScan发生异常",ioex);
        }
        return  resultSet;
    }

    //****************************************************数据库****************************************************

    /**
     * 判断key是否存在
     * @param key 键
     * @return 键是否存在
     */
    public static Boolean exists(String key){
        return redisUtil.redisTemplate.hasKey(key);
    }

    /**
     * 删除单个key
     * @param key 键
     * @return 是否已删除
     */
    public static Boolean del(String key){
        return redisUtil.redisTemplate.delete(key);
    }

    /**
     * 批量删除key
     * @param keys key集合
     * @return 受影响个数
     */
    public static Long del(Set<String> keys){
        return redisUtil.redisTemplate.delete(keys);
    }

    //****************************************************自动过期***************************************************

    /**
     * 设置某个键的生存秒数，只能设置一级键
     * @param key 键值
     * @param ttl 过期秒数
     * @return 是否设置成功
     */
    public static Boolean expire(String key, Long ttl) {
        return redisUtil.redisTemplate.expire(key, ttl, TimeUnit.SECONDS);
    }

    /**
     * 设置某个键的生存毫秒数，只能设置一级键
     * @param key 键
     * @param ttl 过期毫秒数
     * @return 是否设置成功
     */
    public static Boolean pExpire(String key, Long ttl) {
        return redisUtil.redisTemplate.expire(key, ttl, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取key剩余的生存秒数
     * @param key 键
     * @return 剩余的生存秒数
     */
    public static Long ttl(String key) {
        return redisUtil.redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 获取key剩余的生存毫秒数
     * @param key 键
     * @return 剩余的生存毫秒数
     */
    public static Long pTtl(String key) {
        return redisUtil.redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
    }

    /**
     * 指定键在某个时间中过期
     * @param key 键
     * @param date 日期,底层unix时间戳
     * @return 是否设置成功
     */
    public static Boolean expireAt(String key,Date date){
        return redisUtil.redisTemplate.expireAt(key, date);
    }

    /**
     * 移除给定 key 的生存时间，将这个 key 设置为不带生存时间、永不过期
     * @param key 键
     * @return 是否设置成功
     */
    public static Boolean persis (String key){
        return redisUtil.redisTemplate.persist(key);
    }

    private static  <T> Set<T> setFilterNullVal(Collection<Object> collection ,Class<T> clazz){
        Set<T> resultSet;
        if (collection != null && !collection.isEmpty()){
            resultSet = new HashSet<>(collection.size());
            collection.forEach(item ->{
                T tempItem = cast(item,clazz);
                Optional.ofNullable(tempItem).ifPresent(resultSet::add);
            });
        }else {
            resultSet = Collections.emptySet();
        }
        return resultSet;
    }

    private static  <T> List<T> listFilterNullVal(Collection<Object> collection ,Class<T> clazz){
        List<T> resultList;
        if (collection != null && !collection.isEmpty()){
            resultList = new ArrayList<>(collection.size());
            collection.forEach(item ->{
                T tempItem = cast(item,clazz);
                Optional.ofNullable(tempItem).ifPresent(resultList::add);
            });
        }else {
            resultList = Collections.emptyList();
        }
        return resultList;
    }


    /**
     * 类型转换
     * @param obj obj类型参数
     * @param clazz class类型
     * @return 返回指定class的值
     */
    private static <T> T cast(Object obj, Class<T> clazz) {
        try {
            if (obj == null) {
                return null;
            }

            if (clazz == obj.getClass() || clazz.isAssignableFrom(obj.getClass())) {
                return (T) obj;
            }

            if (clazz == byte.class || clazz == Byte.class) {
                return (T) castToByte(obj);
            }

            if (clazz == char.class || clazz == Character.class) {
                return (T) castToChar(obj);
            }

            if (clazz == short.class || clazz == Short.class) {
                return (T) castToShort(obj);
            }

            if (clazz == long.class || clazz == Long.class) {
                return (T) castToLong(obj);
            }

            if (clazz == float.class || clazz == Float.class) {
                return (T) castToFloat(obj);
            }

            if (clazz == double.class || clazz == Double.class) {
                return (T) castToDouble(obj);
            }

            if (clazz == int.class || clazz == Integer.class) {
                return (T) castToInteger(obj);
            }
        }catch (Exception e){
           log.warn("类型转换出错：",e);
        }
        return null;
    }

    private static Byte castToByte(Object value){
        return Byte.parseByte(value.toString());
    }

    private static Character castToChar(Object value){
        return value.toString().charAt(0);
    }

    private static Short castToShort(Object value){
        return Short.parseShort(value.toString());
    }

    private static Float castToFloat(Object value){
        return Float.parseFloat(value.toString());
    }

    private static Long castToLong(Object value){
        return Long.parseLong(value.toString());
    }

    private static Double castToDouble(Object value){
        return Double.parseDouble(value.toString());
    }

    private static Integer castToInteger(Object value){
        return Integer.parseInt(value.toString());
    }
}
